﻿using System;
using System.Linq.Expressions;

namespace SimpleORM
{
    public class DbContext : DataSource, IDbContext
    {
        #region Properties

        public override string ProviderName { get { return DataSource.Name; } }
        public override string ConnectionString { get { return DataSource.ConnectionString; } }

        public IProviderDatabase DataSource { get; private set; }
        public QueryCollection Queries { get; private set; }
        public QuerySettings Settings { get; private set; }

        #endregion

        #region Ctor

        public DbContext(DbContext source)
            : base(source.DataSource.Name, source.DataSource.ConnectionString)
        {
            DataSource = source.DataSource;
            Settings = source.Settings;
            Queries = new QueryCollection(DataSource);
        }

        public DbContext(IProviderDatabase provider)
            : base(provider.Name, provider.ConnectionString)
        {
            DataSource = provider;
            Settings = new QuerySettings();
            Queries = new QueryCollection(provider);
        }

        public DbContext(string cntStringName)
            : this(Provider.Create(cntStringName) as IProviderDatabase)
        {
        }

        public DbContext(string providerName, string cntString)
            : this(Provider.Create(providerName, cntString) as IProviderDatabase)
        {
        }

        protected override void OnInitialize(string providerName, string cntString)
        {
        }

        #endregion

        #region Methods

        #region Methods - Connection

        public void OpenConnection()
        {
            DataSource.Open();
        }

        public void CloseConnection()
        {
            DataSource.Close();
        }

        public void BeginTransaction()
        {
            DataSource.Open();
            DataSource.BeginTransaction();
        }

        public void CommitTransaction()
        {
            DataSource.CommitTransaction();
            DataSource.Close();
        }

        public void RollbackTransaction()
        {
            DataSource.RollbackTransaction();
            DataSource.Close();
        }

        public object SaveChanges()
        {
            return Queries.Save();
        }

        #endregion

        #region Methods - Create

        public override IDataSourceSchema CreateSchema()
        {
            return DataSource.CreateSchema();
        }

        public IQueryContext CreateQuery()
        {
            QueryContext qc = Activator.CreateInstance(
                typeof(QueryContext), DataSource) as QueryContext;

            qc.Settings = Settings;

            return qc;
        }

        public IQueryContext CreateQuery(Type type)
        {
            QueryContext qc = Activator.CreateInstance(
                typeof(QueryContext<>).MakeGenericType(type), DataSource) as QueryContext;

            qc.Settings = Settings;
            return qc as IQueryContext;
            //return (IQueryContext)QuerybaseAOP.Create(qc);
        }

        public IQueryContext<T> CreateQuery<T>()
        {
            return (IQueryContext<T>)CreateQuery(typeof(T));
        }

        public IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                                   object executeArgument,
                                                                   Expression<Func<TResult>> model)
        {
            //ProcedureContext<TResult> qc = Activator.CreateInstance(
            //    typeof(ProcedureContext<TResult>), DataSource) as ProcedureContext<TResult>;

            //qc.Settings = Settings;
            //qc.CommandContext.CommandText.Append(procedureName);
            //qc.CommandContext.ExecuteArgument = executeArgument;

            //return QuerybaseAOP.Create(qc) as IProcedureContext<TResult>;
            return null;
        }

        public IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                                   object executeArgument)
        {
            //ProcedureContext<TResult> qc = Activator.CreateInstance(
            //    typeof(ProcedureContext<TResult>), DataSource) as ProcedureContext<TResult>;

            //qc.Settings = Settings;
            //qc.CommandContext.CommandText.Append(procedureName);
            //qc.CommandContext.ExecuteArgument = executeArgument;

            //return QuerybaseAOP.Create(qc) as IProcedureContext<TResult>;
            return null;
        }

        public IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                                   object executeArgument,
                                                                   eCommandType commandType,
                                                                   Expression<Func<TResult>> model)
        {
            //ProcedureContext<TResult> qc = Activator.CreateInstance(
            //    typeof(ProcedureContext<TResult>), DataSource) as ProcedureContext<TResult>;

            //qc.Settings = Settings;
            //qc.CommandContext.CommandText.Append(procedureName);
            //qc.CommandContext.ExecuteArgument = executeArgument;
            //qc.CommandContext.Type = commandType;

            //return QuerybaseAOP.Create(qc) as IProcedureContext<TResult>;
            return null;
        }

        public IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                                   object executeArgument,
                                                                   eCommandType commandType)
        {
            return null;
            //ProcedureContext<TResult> qc = Activator.CreateInstance(
            //    typeof(ProcedureContext<TResult>), DataSource) as ProcedureContext<TResult>;

            //qc.Settings = Settings;
            //qc.CommandContext.CommandText.Append(procedureName);
            //qc.CommandContext.ExecuteArgument = executeArgument;
            //qc.CommandContext.Type = commandType;

            //return QuerybaseAOP.Create(qc) as IProcedureContext<TResult>;
        }

        #endregion

        #region Methods - Cache

        public void ClearCache()
        {
            //DbCache.Clear(DataSource.ConnectionString);
        }

        //public Cache2D GetCacheInstance()
        //{
        //    return DbCache.Get(DataSource.ConnectionString);
        //}

        #endregion

        #region Methods - Override

        public override object Clone()
        {
            var ds = DataSource.Clone() as IProviderDatabase;

            DbContext db = Activator.CreateInstance(this.GetType(), ds) as DbContext;
            db.Settings = Settings.Clone() as QuerySettings;

            return db;
        }

        #endregion

        #endregion
    }
}
